/* Dump a gcov file, for debugging use.
   Copyright (C) 2002-2021 Free Software Foundation, Inc.
   Contributed by Nathan Sidwell <nathan@codesourcery.com>

Gcov is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

Gcov is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Gcov; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include <stdio.h>
#include <getopt.h>
#include <stdint.h>
#include <assert.h>
#include <inttypes.h>
#include <cstdlib>
#include <cstring>
#include <stdarg.h>
#include "option.h"
#include "mpl_logging.h"
#include "gcov_parser.h"

// macro define used in gcov-io
typedef unsigned char boolean;
#define HOST_WIDE_INT long long
#define CHAR_BIT __CHAR_BIT__

#ifndef false
#define false 0
#endif

#ifndef true
#define true 1
#endif

typedef unsigned gcov_unsigned_t;
typedef int64_t gcov_type;
typedef uint64_t gcov_type_unsigned;
typedef unsigned  location_t;
#ifndef ATTRIBUTE_UNUSED
#define ATTRIBUTE_UNUSED __attribute__((unused))
#endif

#define XNEWVEC(T, N)       ((T *) malloc (sizeof (T) * (N)))
#define XRESIZEVAR(T, P, S) ((T *) realloc ((P), (S)))

/* Return the number of set bits in X.  */
static int popcount_hwi (unsigned HOST_WIDE_INT x) {
  int i, ret = 0;
  size_t bits = sizeof (x) * CHAR_BIT;

  for (i = 0; i < bits; i += 1)
    {
      ret += x & 1;
      x >>= 1;
    }

  return ret;
}

#define input_location 0
#define gcc_assert(EXPR) ((void)(0 && (EXPR)))
#define IS_DIR_SEPARATOR_1(dos_based, c)                \
  (((c) == '/')                             \
   || (((c) == '\\') && (dos_based)))
#define IS_UNIX_DIR_SEPARATOR(c) IS_DIR_SEPARATOR_1 (0, c)
#define IS_DIR_SEPARATOR(c) IS_UNIX_DIR_SEPARATOR (c)
#define xmalloc malloc

static void fatal_error (location_t loc, const char *gmsgid, ...) {
  va_list ap;
  va_start (ap, gmsgid);
  va_end (ap);
  assert(0);
}


#define IN_GCOV (-1)
#include "gcov-io.h"
#include "gcov-io.c"

namespace maple {
static unsigned object_runs;
static unsigned program_count;
//static unsigned bbg_stamp = 0;
// reference
int MGcovParser::read_count_file() {
  std::string gcovDataFile = Options::profile;
  if (gcovDataFile.empty()) {
    const std::string& fileName = m.GetFileName();
    std::string::size_type lastDot = fileName.find_last_of('.');
    ASSERT(lastDot != std::string::npos, "sanity check");
    // search default directory
    if (const char* env_p = std::getenv("GCOV_PREFIX")) {
      gcovDataFile.append(env_p);
    } else {
      gcovDataFile.append(".");
    }
    gcovDataFile.append("/");
    gcovDataFile.append(fileName.substr(0, lastDot));
    gcovDataFile.append(".gcda");
  }
  if (!gcov_open(gcovDataFile.c_str(), 1)) {
    LogInfo::MapleLogger() << "no data file " << gcovDataFile << " \n";
    return 0;
  }

  if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) {
    LogInfo::MapleLogger() <<  gcovDataFile << " not a gcov data file\n";
    gcov_close ();
    return 1;
  }
  unsigned version = gcov_read_unsigned ();
  if (version != GCOV_VERSION) {
    char v[4], e[4];
    GCOV_UNSIGNED2STRING (v, version);
    GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
    LogInfo::MapleLogger() << gcovDataFile <<  " version " << v << " prefer version " << e << "\n";
  }
  unsigned tag = gcov_read_unsigned ();
  gcovData = localMP->New<GcovProfileData>(&alloc);
  GcovFuncInfo *funcInfo = nullptr;
  while ((tag = gcov_read_unsigned ())) {
    unsigned length = gcov_read_unsigned ();
    unsigned long base = gcov_position ();

    if (tag == GCOV_TAG_PROGRAM_SUMMARY) {
      struct gcov_summary summary;
      gcov_read_summary (&summary);
      object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
      program_count++;
    } else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH) {
      unsigned ident;
      /* Try to find the function in the list.  To speed up the
         search, first start from the last function found.  */
      ident = gcov_read_unsigned ();
      unsigned lineno_checksum = gcov_read_unsigned ();
      unsigned cfg_checksum = gcov_read_unsigned ();
      funcInfo = localMP->New<GcovFuncInfo>(&alloc, ident, lineno_checksum, cfg_checksum);
      (gcovData->funcsCounter)[ident] = funcInfo;
    } else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS)) {
      funcInfo->num_counts = GCOV_TAG_COUNTER_NUM(length);
      for (int ix = 0; ix != funcInfo->num_counts; ix++) {
        funcInfo->counts.push_back(gcov_read_counter());
      }
    }
    gcov_sync (base, length);
  }
  gcov_close();

  if (dumpDetail) {
    DumpFuncInfo();
  }
  return 0;
}

void MGcovParser::DumpFuncInfo() {
  for (auto &it : gcovData->funcsCounter) {
    GcovFuncInfo *funcInfo = it.second;
    LogInfo::MapleLogger() << "\nfunction ident " << std::dec << funcInfo->ident;
    LogInfo::MapleLogger() << "  lino_checksum 0x" << std::hex << funcInfo->lineno_checksum;
    LogInfo::MapleLogger() << "  cfg_checksum 0x" << std::hex <<  funcInfo->cfg_checksum << "\n";
    LogInfo::MapleLogger() << "  num_counts " <<  std::dec << funcInfo->num_counts << " : ";
    for (int i = 0; i < funcInfo->num_counts; i++) {
      LogInfo::MapleLogger() << std::dec << "  " << funcInfo->counts[i];
    }
  }
  LogInfo::MapleLogger() << "\n" ;
}

void M2MGcovParser::GetAnalysisDependence(AnalysisDep &aDep) const {
  aDep.SetPreservedAll();
}

bool M2MGcovParser::PhaseRun(maple::MIRModule &m) {
  MemPool *memPool = GetPhaseMemPool();
  MGcovParser gcovParser(m, memPool, true);

  int res = gcovParser.read_count_file();
  if (res) {
    // something wrong
    return false;
  }
  m.SetGcovProfile(gcovParser.GetGcovData());
  return true;
}


} // end namespace maple
